import type { UnwrapRef } from "vue";
import { ref, computed, reactive, watch } from "vue";
import _cloneDeep from "lodash/cloneDeep";
import { PROJECT_SPACE_NAME } from "@/config";

export interface Options {
  /** 是否调试  */
  debug: boolean;
  /** 模块命名空间 */
  moduleKey: string;
  /** 缓存 */
  cache?: boolean;
  /** 缓存实现 */
  storage?: Pick<Storage, "getItem" | "setItem" | "removeItem" | "clear">;
}

/**
 *
 * @param initState 初始状态
 * @param options 配置项
 */
export const useState = <S extends Record<string, any> = Record<string, any>>(
  initState: S | (() => S),
  {
    debug,
    moduleKey,
    cache,
    storage = {
      getItem: (key: string) => uni.getStorageSync(key),
      setItem: (key: string, value: string) => uni.setStorageSync(key, value),
      removeItem: (key: string) => uni.removeStorageSync(key),
      clear: () => uni.clearStorageSync(),
    },
  }: Options = {
    debug: false,
    moduleKey: "",
  },
) => {
  const cacheKey = `${PROJECT_SPACE_NAME}/useState/${moduleKey}`;

  let cacheState: S | undefined;
  if (cache) {
    const str = storage.getItem(cacheKey);
    try {
      cacheState = JSON.parse(str ?? "");
    } catch (e) {}
  }

  // 初始状态
  const stateInit = typeof initState === "function" ? initState() : initState;

  // 获取初始状态
  const getStateInit = () => _cloneDeep(stateInit);

  // 基本状态
  const stateBase = ref(cacheState ? cacheState : getStateInit());

  // 状态
  const state = computed(() => stateBase.value);

  /**
   * 重置状态
   */
  const resetState = () => {
    stateBase.value = getStateInit() as UnwrapRef<S>;
    debug &&
      console.log(`[useState]
      重置 ${moduleKey} store 状态属性
    `);
  };

  /**
   * 更新状态
   */
  const updateState = (newStateObj: Partial<S> = {}) => {
    Object.entries(newStateObj).forEach(([key, newState]) => {
      const currentState: S = stateBase.value as S;
      const oldState = currentState[key];
      if (oldState !== newState) {
        debug &&
          console.log(`[useState]
        ${moduleKey} ${key} 变化更新： ${oldState} => ${newState}
        `);
        currentState[key as keyof S] = newState;
      }
    });
  };

  if (cache) {
    watch(
      stateBase,
      (n) => {
        storage?.setItem(cacheKey, JSON.stringify(n));
      },
      {
        deep: true,
        immediate: true,
      },
    );
  }

  return reactive({
    state,
    getStateInit,
    resetState,
    updateState,
  });
};
